package han.cirno.corrupt.modules.setting

import android.app.Activity
import android.content.Intent
import androidx.activity.compose.LocalActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Slider
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import han.cirno.corrupt.R
import han.cirno.corrupt.activity.main.LocalBottomPadding
import han.cirno.corrupt.activity.main.LocalNavController
import han.cirno.corrupt.activity.main.MainActivity
import han.cirno.corrupt.activity.main.MainActivityViewModel
import han.cirno.corrupt.activity.main.NavItem
import han.cirno.corrupt.activity.main.navigate
import han.cirno.corrupt.compose.VerticalSpacer
import han.cirno.corrupt.compose.localeStringResource
import han.cirno.corrupt.compose.preferenceFlow
import han.cirno.corrupt.compose.rememberPreference
import han.cirno.corrupt.data.SettingColorTheme
import han.cirno.corrupt.data.SettingKeys
import han.cirno.corrupt.getPreferenceKey
import han.cirno.corrupt.writeDataStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import kotlin.reflect.KClass

private val CARD_HEIGHT=84.dp
val LocalDependencyData = staticCompositionLocalOf { mutableStateMapOf<String,Boolean>() }
val LocalConfigData = staticCompositionLocalOf<SettingConfig> { error("No config provided") }

val prefsMapper=HashMap<KClass<*>,@Composable (SettingConfig.PreferenceItem)->Unit>().apply {
    put(SettingConfig.PreferenceItem.Category::class){
        it as SettingConfig.PreferenceItem.Category
        CategoryTitle(it)
        it.preferences.forEach { p-> this[p::class]?.invoke(p) }
    }
    put(SettingConfig.PreferenceItem.Preference.Switch::class) { SwitchSetting(it as SettingConfig.PreferenceItem.Preference.Switch) }
    put(SettingConfig.PreferenceItem.Preference.Normal::class) { NormalSetting(it as SettingConfig.PreferenceItem.Preference.Normal) }
    put(SettingConfig.PreferenceItem.Preference.SeekBar::class) { SeekBarSetting(it as SettingConfig.PreferenceItem.Preference.SeekBar) }
    put(SettingConfig.PreferenceItem.Preference.DropDown::class) { DropdownSetting(it as SettingConfig.PreferenceItem.Preference.DropDown) }
}

val onClickListener=HashMap<String,(Activity?,NavController)->Unit>().apply {
    put(SettingKeys.NORMAL_SELECT_BACKGROUND_IMAGE){ activity,_->
        Intent(Intent.ACTION_GET_CONTENT).also {
            it.type="image/*"
            it.addCategory(Intent.CATEGORY_OPENABLE)
        }.let { (activity as MainActivity).fileOpenLauncher.launch(it) }
    }
    put(SettingKeys.NORMAL_LICENSES) {activity,_->
        activity?.startActivity(Intent(activity, OssLicensesMenuActivity::class.java))
    }
    put(SettingKeys.NORMAL_CUSTOM_SCHEME){_,navController->
        navController.navigate(NavItem.ThemeSettingScreen)
    }
    put(SettingKeys.NORMAL_REPORT){_,navController->
        navController.navigate(NavItem.ReportScreen)
    }
}

val onChangeListener=HashMap<String,(MainActivityViewModel,Any?)->Unit>().apply {
    put(SettingKeys.SELECT_COLOR_SCHEME){model,any->
        any as String
        if (any==SettingColorTheme.BACKGROUND_BASE)
            model.updateColorThemeFromBackground()
    }
}

@Composable
fun SettingLayout(config: SettingConfig,scrollState: ScrollState){
    val sharedData = remember{ mutableStateMapOf<String,Boolean>() }
    CompositionLocalProvider(
        LocalDependencyData provides sharedData,
        LocalConfigData provides config) {
        Column(modifier = Modifier.fillMaxWidth().verticalScroll(scrollState)) {
            Column(modifier = Modifier.fillMaxWidth().padding(8.dp)) {
                config.preferenceItems.forEach {
                    if (it is SettingConfig.PreferenceItem.Category)
                        prefsMapper[it::class]?.invoke(it)
                }
            }
            VerticalSpacer(LocalBottomPadding.current)
        }
    }
}

@Composable
fun CategoryTitle(category: SettingConfig.PreferenceItem.Category){
    Text(
        modifier = Modifier.padding(vertical = 8.dp),
        text=localeStringResource(category.title),
        fontWeight = FontWeight.Bold,
        fontSize = MaterialTheme.typography.bodyLarge.fontSize,
        color = MaterialTheme.colorScheme.primary
    )
}

@Composable
fun SwitchSetting(switch: SettingConfig.PreferenceItem.Preference.Switch){
    var option by rememberPreference(switch.key!!,switch.defaultValue)
    val link = LocalDependencyData.current
    val mainActivityViewModel:MainActivityViewModel= hiltViewModel(LocalActivity.current as AppCompatActivity)
    OutlinedCard (
        enabled = isAvailable(switch),
        modifier = Modifier.fillMaxWidth().height(CARD_HEIGHT).padding(4.dp),
        onClick = {
            option=option.not()
            link[switch.key!!]=option
            onChangeListener[switch.key]?.invoke(mainActivityViewModel,option)
        }) {
        Row(verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier
                .fillMaxSize()
                .padding(horizontal = 16.dp, vertical = 8.dp)) {
            Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.Center) {
                Text(
                    text=localeStringResource(switch.title?: R.string.empty),
                    style = MaterialTheme.typography.bodyMedium,
                    modifier = Modifier.padding(0.dp),)
                val offSummaryAvailable = switch.summaryOff != null && !option
                val onSummaryAvailable = switch.summaryOn != null && option
                val summaryAvailable=switch.summary!=null
                AnimatedVisibility(
                    onSummaryAvailable
                            || offSummaryAvailable
                            || summaryAvailable) {
                    val text:String = if (summaryAvailable)
                        localeStringResource(switch.summary!!)
                    else if (onSummaryAvailable)
                        localeStringResource(switch.summaryOn!!)
                    else if (offSummaryAvailable)
                        localeStringResource(switch.summaryOff!!)
                    else ""
                    Text(
                        modifier = Modifier.padding(0.dp),
                        text=text,
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                        style = MaterialTheme.typography.bodySmall
                    )
                }
            }
            Switch(
                enabled = isAvailable(switch),
                checked = option,
                onCheckedChange = {
                    option = it
                    link[switch.key!!]=option
                    onChangeListener[switch.key]?.invoke(mainActivityViewModel,it)
                                  },
            )
        }
    }
}

@Composable
fun NormalSetting(normal: SettingConfig.PreferenceItem.Preference.Normal){
    val activity= LocalActivity.current
    val navController=LocalNavController.current
    OutlinedCard (
        enabled = isAvailable(normal),
        modifier = Modifier.fillMaxWidth().height(CARD_HEIGHT).padding(4.dp),
        onClick = { onClickListener[normal.key]?.invoke(activity,navController)}) {
        Column (
            modifier = Modifier
                .fillMaxSize()
                .padding(horizontal = 16.dp, vertical = 8.dp),
            verticalArrangement = Arrangement.Center) {
            Text(
                text=localeStringResource(normal.title?: R.string.empty),
                style = MaterialTheme.typography.bodyMedium)
            val summaryAvailable=normal.summary!=null
            AnimatedVisibility(summaryAvailable) {
                val text:String = if (summaryAvailable)
                    localeStringResource(normal.summary!!)
                else ""
                Text(
                    text=text,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    style = MaterialTheme.typography.bodySmall
                )
            }
        }
    }
}

@Composable
fun SeekBarSetting(seekBar: SettingConfig.PreferenceItem.Preference.SeekBar){
    //var value by rememberPreference(seekBar.key!!,seekBar.defaultValue)
    //var valueShow= value+0
    var valueShow by remember { mutableIntStateOf(runBlocking { preferenceFlow(seekBar.key!!,seekBar.defaultValue).first()})}
    val mainActivityViewModel:MainActivityViewModel= hiltViewModel(LocalActivity.current as AppCompatActivity)
    OutlinedCard (
        enabled = isAvailable(seekBar),
        modifier = Modifier.fillMaxWidth().padding(4.dp),
        onClick = { }) {
        Column (
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 16.dp, vertical = 16.dp),
            verticalArrangement = Arrangement.Center) {
            Text(
                text=localeStringResource(seekBar.title?: R.string.empty),
                style = MaterialTheme.typography.bodyMedium)
            val summaryAvailable=seekBar.summary!=null
            AnimatedVisibility(summaryAvailable) {
                val text:String = if (summaryAvailable)
                    localeStringResource(seekBar.summary!!)
                else ""
                Text(
                    text=text,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    fontSize = MaterialTheme.typography.bodySmall.fontSize
                )
            }
            VerticalSpacer(8.dp)
            Slider(
                enabled = isAvailable(seekBar),
                modifier = Modifier.fillMaxWidth().height(16.dp),
                value=(valueShow-seekBar.min)/(seekBar.max-seekBar.min).toFloat(),
                onValueChange = {
                    val newValue=(it*(seekBar.max-seekBar.min)).toInt()+seekBar.min
                    runBlocking { writeDataStore(getPreferenceKey<Int>(seekBar.key!!),newValue) }
                    valueShow=newValue
                    onChangeListener[seekBar.key]?.invoke(mainActivityViewModel,it)
                })
        }
    }
}

@Composable
fun DropdownSetting(dropDown: SettingConfig.PreferenceItem.Preference.DropDown){
    var value by rememberPreference(dropDown.key!!,dropDown.defaultValue?:"")
    var menuExpanded by rememberSaveable(key = dropDown.key+"_dropDown") { mutableStateOf(false) }
    val mainActivityViewModel:MainActivityViewModel= hiltViewModel(LocalActivity.current as AppCompatActivity)
    Column {
        OutlinedCard (
            enabled = isAvailable(dropDown),
            modifier = Modifier.fillMaxWidth().height(CARD_HEIGHT).padding(4.dp),
            onClick = { menuExpanded=true }) {
            Column (
                verticalArrangement = Arrangement.Center,
                modifier = Modifier
                    .fillMaxSize()
                    .padding(horizontal = 16.dp, vertical = 8.dp)) {
                Text(
                    text=localeStringResource(dropDown.title?: R.string.empty),
                    style = MaterialTheme.typography.bodyMedium)
                val summaryAvailable=dropDown.summary!=null
                val simpleSummaryAvailable=dropDown.simpleSummary
                AnimatedVisibility(summaryAvailable||simpleSummaryAvailable) {
                    val text:String = if (summaryAvailable)
                        localeStringResource(dropDown.summary!!)
                    else if(simpleSummaryAvailable)
                        dropDown[value]?:"<Invalid>"
                    else ""
                    Text(
                        text=text,
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                        style = MaterialTheme.typography.bodySmall
                    )
                }
            }
        }
        DropdownMenu(
            expanded = menuExpanded,
            onDismissRequest = {menuExpanded=false}) {
            dropDown.entries.forEachIndexed { i,e->
                DropdownMenuItem(
                    text = { Text(text = e) },
                    onClick = {
                        value=dropDown.entryValues[i]
                        onChangeListener[dropDown.key]?.invoke(mainActivityViewModel,value)
                        menuExpanded=false
                    }
                )
            }
        }
    }
}

@Composable
fun isAvailable(preference: SettingConfig.PreferenceItem.Preference):Boolean {
    return LocalDependencyData.current[preference.dependency ?: return true] ?: run {
        val value by rememberPreference(key = preference.dependency,
            run run2@{
                ((LocalConfigData.current.findElementByKey(preference.dependency) ?: return@run2 false)
                        as SettingConfig.PreferenceItem.Preference.Switch).defaultValue})
        value
    }
}